module GhCalcUtils where

import System.Directory
import System.IO
import System.Posix.IO
import Data.Char
import System.Random
import Data.List 
import Control.Exception as CE
import System.Exit

evaluate_postfix ::[String] -> Double
evaluate_postfix [] = 0
evaluate_postfix postfix_list = evaluate_postfix_helper postfix_list []


evaluate_postfix_helper :: [String]->[String]->Double
evaluate_postfix_helper [] stack = str_to_num (last stack) 0
evaluate_postfix_helper (x:postfix_expr) stack = 
    let last_stack = last (stack)
        last_butone_stack = last (take (length stack -1) stack)
    in
        if (x=="+" || x == "-" || x == "*" || x =="/" || x=="^")
        then
            if (x == "^")
            then
                let  cur_result = do_op_exp last_butone_stack last_stack
                in
                    evaluate_postfix_helper postfix_expr ((pop_stack stack 2) ++   (num_to_str(cur_result) :[]))
            else
                let cur_result = do_op x last_butone_stack last_stack
                in
                    evaluate_postfix_helper postfix_expr ((pop_stack stack 2) ++   (num_to_str(cur_result) :[]))        
        else
                evaluate_postfix_helper postfix_expr (stack ++ (x:[]))


do_op :: String -> String -> String -> Double
do_op operation op1 op2 = let 
			      num1 = str_to_num op1 0
			      num2 = str_to_num op2 0
			  in
			      case operation of
			      "+" -> (+) num1  num2
			      "-" -> num1 - num2
			      "*" -> num1 * num2
			      "/" -> num1 / num2
			      -- handle mod  and  ^ operation separately
do_op_exp op1 op2 = let
                num1 = (read op1)::Double
                num2 = (read (takeWhile (/='.') op2))::Integer -- reading 0.xxx as Integer causes no parse :)
               in
                   num1 ^ num2

num_to_str :: (Fractional a) => a -> String
num_to_str num = show num

str_to_num :: (Integral a)=> String -> a -> Double
str_to_num [] _  = 0
str_to_num xs count = read xs::Double

pop_stack :: [String] -> Int -> [String]
pop_stack [] _ = []
pop_stack stack count = take (length stack - count) stack

digitToDouble :: Char -> Double
digitToDouble x = case x of
		  '1' -> 1.0
		  '2' -> 2.0
		  '3' -> 3.0
		  '4' -> 4.0
		  '5' -> 5.0
		  '6' -> 6.0
		  '7' -> 7.0
		  '8' -> 8.0
		  '9' -> 9.0
		  '0' -> 0.0
		  otherwise -> 0.0

getRandomString :: IO String
getRandomString = do
    let listLen = length messageList
    let rand =  getStdRandom (randomR (0, (listLen-1)))
    index <- rand 
    return (messageList !! index)

messageList = ["hello world"]

-- TODO In all the code that follows, handle exceptions
-- TODO Put platform independent path separator
getConfigDir :: IO FilePath
getConfigDir = do
  homeDirPath <- getHomeDirectory
  let configDirPath = homeDirPath ++ "/.ghcalc"
  createDirectoryIfMissing False configDirPath
  return configDirPath
 
getSessionFileName :: IO String
getSessionFileName = do
  -- Check the existence of your config directory
  configDirPath <- getConfigDir
  let sessionFilePath = configDirPath ++ "/session.txt"
  handle <- openFile sessionFilePath AppendMode
  hClose handle
  return sessionFilePath

getGladeFilePath :: IO String
getGladeFilePath = do
    -- Check the existence of bin directory
    homeDirPath <- getHomeDirectory
    return (homeDirPath ++ "/bin/ghcalc.glade")

-- I want IO Bool, but openFd fails anyway. Best approach is to use exceptions
-- and propagate values of known type to the enclosing expression
createLockFile :: IO Bool
createLockFile = do
  configDirPath <- getConfigDir 
  let lockFilePath = configDirPath ++ "/lock"
-- This API is clumsy. I do not understand the Maybe type's purpose here
-- Using (Just 0) did not work. Had to use (Just 1). But what is Nothing
-- being used for anyway.
  fd <- openFd lockFilePath WriteOnly (Just 0o755) (OpenFileFlags False False False False False)
  (setLock fd (WriteLock, AbsoluteSeek, 0,0))
  return True 

lockExceptionHandler :: CE.Exception -> IO ()
lockExceptionHandler x = do
	putStrLn "Instance already running"
	exitFailure
